﻿using AutoMapper;
using BaseRepository.Redis;
using MediatR;
using Microsoft.Extensions.DependencyInjection;
using PLC.Domain.Dto;
using PLC.Domain.GlobConfig;
using PLC.Domain.Models;
using PLC.Domain.Service.Service;
using Serilog;

namespace PLC.Domain.ScheduledTasks.Job;

public class PLCReadAndWriteJob:IPLCReadAndWriteJob
{
    // 私有变量  是scope 所以每次的参数是隔离的
    private Dictionary<long,PLCConfig> _plcConfigDic = new Dictionary<long, PLCConfig>();
   
    //依赖注入
    private readonly IMediator _mediator;
    private readonly IRedisRepository _redisRepository;
    private readonly DataConfigService _dataConfigService;
    private readonly DeviceConfigService _deviceConfigService;
    private readonly IMapper _mapper;
    private readonly IServiceProvider _serviceProvider;

    public PLCReadAndWriteJob(IMediator mediator,
        IRedisRepository redisRepository,
        DataConfigService dataConfigService,
        DeviceConfigService deviceConfigService,
        IMapper mapper,
        IServiceProvider serviceProvider)
    {
        _mediator = mediator?? throw new ArgumentNullException(nameof(mediator));
        _redisRepository = redisRepository?? throw new ArgumentNullException(nameof(redisRepository));
        _dataConfigService = dataConfigService ?? throw new ArgumentNullException(nameof(dataConfigService));
        _deviceConfigService = deviceConfigService?? throw new ArgumentNullException(nameof(deviceConfigService));
        _mapper = mapper?? throw new ArgumentNullException(nameof(mapper));
        _serviceProvider = serviceProvider ?? throw new ArgumentNullException(nameof(serviceProvider));
    }

    /// <summary>
    /// 定时任务运行的方法
    /// </summary>
    /// <returns></returns>
    public async Task Work()
    {
        // 获取配置信息
        await GetConfig();
        // 开线程去读取数据
        var tasks = new List<Task<PLCReadModel>>();
        foreach (KeyValuePair<long, PLCConfig> plcConfig in _plcConfigDic)
        {
            tasks.Add(new Task<PLCReadModel>(() =>
            {
                try
                {
                    var plcService = _serviceProvider.GetService<PLCService>();
                    ArgumentNullException.ThrowIfNull(plcService);
                    return plcService.Work(plcConfig.Value);
                }
                catch (Exception e)
                {
                    Log.Error(e,"定时任务执行报错");
                    return new PLCReadModel();
                }
                // 处理逻辑
            }));
        }
        // 事实上这边是  所有的设备都是一起开始  一起结束的  所以可以通过这种判断  之前的线程有没有执行完成
        if (tasks.Count == 0)
        {
            return;
        }
        //启动线程
        foreach (var task in tasks)
        {
            task.Start();
        }

        //等待所有的task执行完成 并且获取返回的数据
        var plcReadModels = await Task.WhenAll(tasks);
        //在这边触发事件 统一的去处理数据
        await _mediator.Publish(new DeviceDataGroupHandleModel()
        { PlcReadModels = plcReadModels });
    }
    /// <summary>
    /// 获取配置
    /// </summary>
    /// <returns></returns>
    private async Task GetConfig()
    {
        var plcConfigs =  await _redisRepository.StringGet<Dictionary<long, PLCConfig>>(RedisKey.PLCConfigs);
        if (plcConfigs==null)
        {
            // 从数据库中获取
            var deviceDatas = await _deviceConfigService.FindPLCDeviceAll();
            var ids = deviceDatas.Select(p => p.Id).ToList();
            var dataConfigs = await _dataConfigService.FindAll(ids);
            // 组合数据
            var dic = new Dictionary<long, PLCConfig>();
            foreach (var deviceData in deviceDatas)
            {
                var dataConfigList = dataConfigs.Where(p => p.DeviceConfigId == deviceData.Id).ToList();
                // 组装plcconfig的数据
                var plcConfig = new PLCConfig()
                    .SetDeviceConfig(deviceData)
                    .SetDataConfigs(_mapper.Map<List<DtoDataConfig>>(dataConfigList))
                    .HandleDataConfigs();
                dic.Add(deviceData.Id,plcConfig);
            }
            // 存入redis
            await _redisRepository.StringSet(RedisKey.PLCConfigs, dic);
            this._plcConfigDic = dic;
        }
        else
        {
            this._plcConfigDic = plcConfigs;
        }
    }
    
   
}